package com.ndood.admin.controller.user;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.ProviderSignInAttempt;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.ModelAndView;

import com.ndood.admin.core.constaints.AdminErrCode;
import com.ndood.admin.core.exception.AdminException;
import com.ndood.admin.core.properties.AdminProperties;
import com.ndood.admin.core.web.tools.EmailUtils;
import com.ndood.admin.core.web.tools.SessionUtils;
import com.ndood.admin.pojo.comm.vo.AdminResultVo;
import com.ndood.admin.pojo.system.dto.UserDto;
import com.ndood.admin.service.user.AccountInfoService;
import com.ndood.core.utils.AesEncryptUtil;
import com.ndood.core.utils.RegexUtils;
import com.ndood.core.validate.code.ValidateCodeException;
import com.ndood.core.validate.code.ValidateCodeProcessorHolder;
import com.ndood.core.validate.code.ValidateCodeType;

import cn.hutool.core.codec.Base64;
import lombok.extern.slf4j.Slf4j;

/**
 * 创建UserController
 * 
 * @author ndood
 */
@Controller
@Slf4j
public class UserAuthController {

	Logger logger = LoggerFactory.getLogger(getClass());
	
	@Autowired
	private AdminProperties adminProperties;

	@Autowired
	private EmailUtils emailUtils;

	@Autowired
	private SessionUtils sessionUtils;

	@Autowired
	private AccountInfoService accountInfoService;

	@Autowired
	private ProviderSignInUtils providerSignInUtils;

	/*
	 * @Autowired private AppSignUpUtils appSignUpUtils;
	 */

	private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

	@Autowired
	private ConnectionFactoryLocator connectionFactoryLocator;

	/**
	 * 注册后自动登录
	 */
	@Autowired
	private AuthenticationManager authenticationManager;

	/**
	 * 系统中的校验码处理器
	 */
	@Autowired
	private ValidateCodeProcessorHolder validateCodeProcessorHolder;

	/**
	 * 检查手机号是否存在
	 */
	@RequestMapping("/user/check_mobile")
	@ResponseBody
	public AdminResultVo checkMobile(String mobile) {
		boolean ok = accountInfoService.checkMobile(mobile);
		if (ok) {
			return AdminResultVo.ok().setMsg("手机号有效！");
		}
		return AdminResultVo.error().setMsg("手机号已存在！");
	}

	/**
	 * 检查图形验证码是否有效
	 * 
	 * @throws Exception
	 */
	@RequestMapping("/user/check_send_sms")
	@ResponseBody
	public AdminResultVo checkImageCode(HttpServletRequest request, HttpServletResponse response, String imageCode,
			Integer resend) throws Exception {
		logger.info("校验请求(" + request.getRequestURI() + ")中的验证码,验证码类型IMAGE");
		try {
			ServletWebRequest req = new ServletWebRequest(request, response);
			if (resend == 0) {
				validateCodeProcessorHolder.findValidateCodeProcessor(ValidateCodeType.IMAGE).validate(req);
			}
			validateCodeProcessorHolder.findValidateCodeProcessor(ValidateCodeType.SMS).create(req);
			return AdminResultVo.ok().setMsg("发送短信成功！");
		} catch (ValidateCodeException exception) {
			return AdminResultVo.error().setMsg("发送短信失败，验证码无效！");
		}
	}
	
	/**
	 * 检查密码是否正确
	 */
	@RequestMapping("/user/check_pass")
	@ResponseBody
	public AdminResultVo checkPassword(HttpServletRequest request, HttpServletResponse response, String pass) throws Exception {
		
		logger.debug("Step1: 校验请求参数");
		if(StringUtils.isBlank(pass)) {
			return AdminResultVo.error().setMsg("密码不能为空！");
		}
		
		logger.debug("Step2: 校验密码");
		UserDetails userInfo = sessionUtils.getLoginUserInfo();
		if(userInfo==null) {
			return AdminResultVo.error().setMsg("当前用户未登录！");
		}
		String userId = userInfo.getUsername();
		boolean ok = accountInfoService.checkPassword(userId,pass);
		if(!ok) {
			return AdminResultVo.error().setMsg("密码错误！");
		}
		return AdminResultVo.ok().setMsg("验证成功！");
		
	}

	/**
	 * 去社交注册页 需要获取connection信息
	 */
	@RequestMapping("/user/to_social_regist")
	@ResponseBody
	public ModelAndView toSocialRegist(HttpServletRequest request) {
		ProviderSignInAttempt atempt = (ProviderSignInAttempt) sessionStrategy
				.getAttribute(new ServletWebRequest(request), ProviderSignInAttempt.SESSION_ATTRIBUTE);
		Connection<?> connection = atempt.getConnection(connectionFactoryLocator);
		String displayName = connection.getDisplayName();
		String imageUrl = connection.getImageUrl();

		Map<String, Object> model = new HashMap<String, Object>();
		model.put("displayName", displayName);
		model.put("imageUrl", imageUrl);

		String socialUserId = connection.getKey().toString();
		if (socialUserId.contains("qq")) {
			return new ModelAndView("user/social/to_regist_qq", model);
		} else {
			return new ModelAndView("user/social/to_regist_weixin", model);
		}
	}

	/**
	 * 注册逻辑
	 */
	@PostMapping("/user/social_regist")
	@ResponseBody
	@Transactional
	public AdminResultVo regist(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 判断短信验证码
		try {
			validateCodeProcessorHolder.findValidateCodeProcessor(ValidateCodeType.SMS)
					.validate(new ServletWebRequest(request, response));
		} catch (ValidateCodeException exception) {
			return AdminResultVo.error().setMsg("短信验证码无效！");
		}

		// 不管注册用户还是绑定用户，都会得到用户唯一标识
		UserDto dto = new UserDto();
		dto.setNickName(request.getParameter("nickName"));
		dto.setPassword(request.getParameter("password"));
		dto.setHeadImgUrl(request.getParameter("image"));
		dto.setMobile(request.getParameter("mobile"));
		String userId = accountInfoService.registerAccount(dto);

		// 把userId传给springSocial，springSocial会把userId和获取到的openId一块插到imooc_userconnection表里去
		providerSignInUtils.doPostSignUp(userId, new ServletWebRequest(request));

		// app演示使用app配置
		// appSignUpUtils.doPostSignUp(new ServletWebRequest(request), userId);

		// 注册成功后自动登录
		try {
			UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(dto.getUsername(),
					dto.getPassword());
			authRequest.setDetails(dto); // 密码没经过加密才可以通过
			Authentication authenticatedUser = authenticationManager.authenticate(authRequest);
			SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
			request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
					SecurityContextHolder.getContext());

		} catch (Exception e) {
			logger.error("注册后自动登录失败:" + e.getMessage(), e);
		}

		return AdminResultVo.ok().setMsg("新用户注册成功！");
	}

	/**
	 * 去登陆
	 */
	@GetMapping("/user/to_login")
	public String toSignIn() {
		UserDetails userDetails = sessionUtils.getLoginUserInfo();
		if (userDetails == null) {
			return "user/admin-signIn";
		}
		return "redirect:/index";
	}

	/**
	 * 去邮箱注册
	 */
	@GetMapping("/user/to_regist")
	public String toSignUpMobile() {
		return "user/admin-signUp";
	}

	/**
	 * 通过手机号注册
	 */
	@PostMapping("/user/regist/mobile")
	@ResponseBody
	public AdminResultVo registByMobile(HttpServletRequest request, HttpServletResponse response) throws Exception {

		log.debug("Step1: 判断手机格式是否合法");
		String username = request.getParameter("username");
		boolean isMobile = RegexUtils.checkMobile(username);
		if (!isMobile) {
			return AdminResultVo.error().setMsg("手机格式不合法！");
		}

		log.debug("Step2: 校验手机是否存在");
		boolean isNotExists = accountInfoService.checkMobile(username);
		if (!isNotExists) {
			return AdminResultVo.error().setMsg("手机号已存在！");
		}

		log.debug("Step3: 校验验证码是否有效");
		try {
			validateCodeProcessorHolder.findValidateCodeProcessor(ValidateCodeType.IMAGE)
					.validate(new ServletWebRequest(request, response));
		} catch (ValidateCodeException exception) {
			return AdminResultVo.error().setMsg("图片验证码无效！");
		}

		return AdminResultVo.ok().setMsg("手机号注册成功！");
	}

	/**
	 * 通过邮箱注册
	 */
	@PostMapping("/user/regist/email")
	@ResponseBody
	@Transactional
	public AdminResultVo registByEmail(HttpServletRequest request, HttpServletResponse response) throws Exception {

		log.debug("Step1: 判断邮箱格式是否合法");
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		if(StringUtils.isBlank(username)) {
			return AdminResultVo.error().setMsg("邮箱不能为空！");
		}
		if(StringUtils.isBlank(password)) {
			return AdminResultVo.error().setMsg("密码不能为空！");
		}
		
		boolean isEmail = RegexUtils.checkEmail(username);
		if (!isEmail) {
			return AdminResultVo.error().setMsg("邮箱格式不合法！");
		}

		log.debug("Step2: 校验邮箱是否存在");
		boolean isNotExists = accountInfoService.checkEmail(username);
		if (!isNotExists) {
			return AdminResultVo.error().setMsg("邮箱已存在，请到邮箱里激活！");
		}

		log.debug("Step3: 校验验证码是否有效");
		try {
			validateCodeProcessorHolder.findValidateCodeProcessor(ValidateCodeType.IMAGE)
					.validate(new ServletWebRequest(request, response));
		} catch (ValidateCodeException exception) {
			return AdminResultVo.error().setMsg("图片验证码无效！");
		}

		log.debug("Step4: 注册一个用户");
		UserDto dto = new UserDto();
		dto.setEmail(username);
		dto.setPassword(password);
		// dto.setUsername(dto.getEmail());
		dto.setEmailStatus(0);
		String userId = accountInfoService.registerAccount(dto);

		log.debug("Step5: 发送激活邮件");
		String aesKey = adminProperties.getEmail().getEncryptKey();
		String from = adminProperties.getEmail().getFrom();
		String domain = adminProperties.getDomain();
		String activateUrl = adminProperties.getEmail().getActivationUrl();
		String active = AesEncryptUtil.encrypt(userId + "_" + domain + "_" + from, aesKey);
		String linkid = URLEncoder.encode(Base64.encode(userId, "UTF-8"), "UTF-8");
		emailUtils.sendActivationEmail(username, from, "payboyPlus激活邮件", username, domain, activateUrl, active,
				linkid);
		return AdminResultVo.ok().setMsg("邮箱注册成功！");

	}

	/**
	 * 通过手机号注册完成
	 */
	@PostMapping("/user/regist/mobile_finish")
	@ResponseBody
	@Transactional
	public AdminResultVo registByMobileFinish(HttpServletRequest request, HttpServletResponse response)
			throws Exception {

		log.debug("Step1: 判断手机格式是否合法");
		String mobile = request.getParameter("mobile");
		String password = request.getParameter("password");
		if(StringUtils.isBlank(mobile)) {
			return AdminResultVo.error().setMsg("手机号不能为空！");
		}
		if(StringUtils.isBlank(password)) {
			return AdminResultVo.error().setMsg("密码不能为空！");
		}
		
		boolean isMobile = RegexUtils.checkMobile(mobile);
		if (!isMobile) {
			return AdminResultVo.error().setMsg("手机格式不合法！");
		}

		log.debug("Step2: 校验手机是否存在");
		boolean isNotExists = accountInfoService.checkMobile(mobile);
		if (!isNotExists) {
			return AdminResultVo.error().setMsg("手机号已存在！");
		}

		log.debug("Step3: 校验验证码是否有效");
		try {
			validateCodeProcessorHolder.findValidateCodeProcessor(ValidateCodeType.SMS)
					.validate(new ServletWebRequest(request, response));
		} catch (ValidateCodeException exception) {
			return AdminResultVo.error().setMsg("短信验证码无效！");
		}

		log.debug("Step4: 注册一个用户");
		UserDto dto = new UserDto();
		dto.setMobile(mobile);
		dto.setPassword(password);
		// dto.setUsername(dto.getMobile());
		dto.setEmailStatus(0);
		accountInfoService.registerAccount(dto);
		return AdminResultVo.ok().setMsg("手机号注册成功！");

	}

	/**
	 * 通过手机号注册完成 激活邮箱
	 */
	@GetMapping("/user/regist/email_finish")
	@Transactional
	public String registByEmailFinish(String active, String linkid) throws Exception {

		String userId = null;
		try {
			// Step1: 校验请求参数
			if (StringUtils.isBlank(active)) {
				throw new AdminException(AdminErrCode.ERR_PARAM, "active不能为空！");
			}
			if (StringUtils.isBlank(linkid)) {
				throw new AdminException(AdminErrCode.ERR_PARAM, "linkid不能为空！");
			}
			userId = URLDecoder.decode(new String(Base64.decode(linkid, "UTF-8")), "UTF-8");

			// Step2: 验签
			UserDto accountInfo = accountInfoService.getAccountSimpleInfoById(userId);
			if (accountInfo == null) {
				throw new AdminException(AdminErrCode.ERR_OTHER, "激活失败，邮箱不存在！");
			}
			String aesKey = adminProperties.getEmail().getEncryptKey();
			String from = adminProperties.getEmail().getFrom();
			String domain = adminProperties.getDomain();

			String localActive = AesEncryptUtil
					.encrypt(accountInfo.getId() + "_" + domain + "_" + from, aesKey);
			if (!localActive.equals(active)) {
				throw new AdminException(AdminErrCode.ERR_OTHER, "激活失败，验签失败！");
			}

			// Step3: 判断邮箱是否已激活
			if(accountInfo.getEmailStatus()==1) {
				throw new AdminException(AdminErrCode.ERR_OTHER, "激活失败，改邮箱已被激活，请直接登录！");
			}
			
			// Step4: 修改邮箱的激活状态为已激活
			accountInfoService.activeEmail(accountInfo.getEmail());
			
			// Step5: 激活成功，跳转到注册成功页面
			return "redirect:/user/regist/to_success?type=email&username=" + accountInfo.getEmail();

		} catch (Exception e) {
			e.printStackTrace();
			return "redirect:/user/regist/to_failed?type=email&errMsg=" + e.getMessage();
		}

	}

	/**
	 * 注册成功跳转
	 */
	@GetMapping("/user/regist/to_success")
	public String signUpSuccess(String type, String username, Model model) throws Exception {
		model.addAttribute("type", type);
		if ("email".equals(type)) {
			model.addAttribute("email", username);
		} else if ("mobile".equals(type)) {
			model.addAttribute("mobile", username);
		} else {
			String msg = "无效的注册类型！";
			return "redirect:/user/regist/to_failed?type=email?errMsg=" + URLEncoder.encode(msg, "utf-8");
		}
		return "user/admin-signUp-success";
	}

	/**
	 * 注册失败跳转
	 */
	@GetMapping("/user/regist/to_failed")
	public String signUpFailed(String type, String errMsg, Model model) throws AdminException {
		model.addAttribute("type", type);
		if ("email".equals(type)) {
			model.addAttribute("errMsg", errMsg);
		} else if ("mobile".equals(type)) {
			model.addAttribute("errMsg", errMsg);
		} else {
			throw new AdminException(AdminErrCode.ERR_PARAM,errMsg);
		}
		return "user/admin-signUp-failed";
	}
}
